// SPDX-License-Identifier: GPL-2.0-or-later
/*
 *  BlueZ - Bluetooth protocol stack for Linux
 *
 *  Copyright (C) 2014  Google Inc.
 *
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#define _GNU_SOURCE
#include <stdio.h>
#include <stdbool.h>
#include <stdint.h>
#include <time.h>
#include <stdlib.h>
#include <getopt.h>
#include <unistd.h>
#include <errno.h>

#include "lib/bluetooth.h"
#include "lib/hci.h"
#include "lib/hci_lib.h"
#include "lib/l2cap.h"
#include "lib/uuid.h"

#include "src/shared/mainloop.h"
#include "src/shared/util.h"
#include "src/shared/att.h"
#include "src/shared/queue.h"
#include "src/shared/timeout.h"
#include "src/shared/gatt-db.h"
#include "src/shared/gatt-server.h"

// #include "Rk_wifi.h"
#include "gm_wifi.h"

#define UUID_XIAOFEIXIA			0x0068
#define UUID_XIAOFEIXIA_WRITE_WIFIINFO		0x6801
#define UUID_XIAOFEIXIA_NOTIFY_STATUS		0x6802


#define ATT_CID 4

#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif

#define COLOR_OFF	"\x1B[0m"
#define COLOR_RED	"\x1B[0;91m"
#define COLOR_GREEN	"\x1B[0;92m"
#define COLOR_YELLOW	"\x1B[0;93m"
#define COLOR_BLUE	"\x1B[0;94m"
#define COLOR_MAGENTA	"\x1B[0;95m"
#define COLOR_BOLDGRAY	"\x1B[1;30m"
#define COLOR_BOLDWHITE	"\x1B[1;37m"

static const char test_device_name[] = "XiaoFeiXia";
static bool verbose = true;
extern int http_user_bind();

/********bluez5******************/
struct server {
	int fd;
	struct bt_att *att;
	struct gatt_db *db;
	struct bt_gatt_server *gatt;

	uint8_t *device_name;
	size_t name_len;

	uint16_t gatt_svc_chngd_handle;
	bool svc_chngd_enabled;

	uint16_t hr_handle;
	uint16_t hr_msrmt_handle;
	uint16_t hr_energy_expended;
	bool hr_visible;
	bool hr_msrmt_enabled;
	int hr_ee_count;
};
struct server *server = NULL;

static void att_disconnect_cb(int err, void *user_data)
{
	printf("Device disconnected: %s\n", strerror(err));

	mainloop_quit();
}

static void att_debug_cb(const char *str, void *user_data)
{
	const char *prefix = user_data;

	printf(COLOR_BOLDGRAY "%s" COLOR_BOLDWHITE "%s\n" COLOR_OFF, prefix,
									str);
}

static void gatt_debug_cb(const char *str, void *user_data)
{
	const char *prefix = user_data;

	printf(COLOR_GREEN "%s%s\n" COLOR_OFF, prefix, str);
}

char ssid_array_info[64] = {0};
char passwd_array_info[64] = {0};
char userid_array_info[128] = {0};
int ssid_receive_flag = 0;
int passwd_receive_flag = 0;
int userid_receive_flag = 0;
static void xf_write_wifiinfo_cb(struct gatt_db_attribute *attrib,
	unsigned int id, uint16_t offset,
	const uint8_t *value, size_t len,
	uint8_t opcode, struct bt_att *att,
	void *user_data)
{
	struct server *server = user_data;
	uint8_t ecode = 0;

	if (len <= 0) {
		printf("xf_write_wifiinfo_cb, len <=0, len = %d\n", len);
		return;
	}

	printf("wqy 00 xf_write_wifiinfo_cb, len = %d\n", len);
	for (int i = 0; i < len; i++) {
		printf("%0x ", value[i]);
	}
	printf("\n");

	if (value[0] != 0x55) {
		printf("xf_write_wifiinfo_cb, receive wdata is not ifi info\n");
		ecode = 80;  //暂时不知道设置为啥
		goto done;
	}

	int data_type = value[1];
	int data_packet_index = value[2];
	int data_packet_sum = value[3];
	int data_payload = value[4];

	if (data_payload != (len - 5 - 1)) {
		printf("xf_write_wifiinfo_cb, data_payload len is error");
		goto done;
	}

	printf("xf_write_wifiinfo_cb, data_type = 0x%x\n", data_type);
	if (data_type == 0x1E) {
		printf("query pei wang status...\n");
		notify_wifi_status(0x1E, 0x21);
		goto done;
	}

	char *avalible_data = value + 5;
	printf("xf_write_wifiinfo_cb, receive data is wifi info\n\n");
	if (data_type == 0x01) {
		printf("xf_write_wifiinfo_cb, ssid info\n");
		static int ssid_array_offset = 0;
		static int ssid_array_last_index = -1;
		
		//data_packet_sum为包总数
		ssid_array_last_index++;
		//data_packet_index为包序号
		if (data_packet_index == ssid_array_last_index) {
			//还需要校验一下，记得
			memcpy(ssid_array_info + ssid_array_offset, avalible_data, data_payload);
			ssid_array_offset += data_payload;

			if (data_packet_index == (data_packet_sum - 1)) {
				printf("data type receive complete, ssid = %s\n", ssid_array_info);
				ssid_receive_flag = 1;
				ssid_array_offset = 0;
				ssid_array_last_index = -1;
			}
		}
	}

	if (data_type == 0x02) {
		printf("xf_write_wifiinfo_cb, passwd info\n");
		static int passwd_array_offset = 0;
		static int passwd_array_last_index = -1;
		
		//data_packet_sum为包总数
		passwd_array_last_index++;
		//data_packet_index为包序号
		if (data_packet_index == passwd_array_last_index) {
			//还需要校验一下，记得
			memcpy(passwd_array_info + passwd_array_offset, avalible_data, data_payload);
			passwd_array_offset += data_payload;

			if (data_packet_index == (data_packet_sum - 1)) {
				printf("data type receive complete, passwd = %s\n", passwd_array_info);
				passwd_receive_flag = 1;
				passwd_array_offset = 0;
				passwd_array_last_index = -1;
			}
		}
	}

	if (data_type == 0x04) {
		printf("xf_write_wifiinfo_cb, userid info\n");
		static int userid_array_offset = 0;
		static int userid_array_last_index = -1;
		
		//data_packet_sum为包总数
		userid_array_last_index++;
		//data_packet_index为包序号
		if (data_packet_index == userid_array_last_index) {
			//还需要校验一下，记得
			memcpy(userid_array_info + userid_array_offset, avalible_data, data_payload);
			userid_array_offset += data_payload;

			if (data_packet_index == (data_packet_sum - 1)) {
				printf("data type receive complete, userid = %s\n", userid_array_info);
				userid_receive_flag = 1;
				userid_array_offset = 0;
				userid_array_last_index = -1;
			}
		}
	}

	if (ssid_receive_flag && passwd_receive_flag && userid_receive_flag) {
		printf("ssid, passwd, userid receive complete!\n");
		notify_wifi_status(0x03, 0x30);
		notify_wifi_status(0x03, 0x32);

		int ret = connect_wifi(ssid_array_info, passwd_array_info);
		if (ret == 0) {
			printf("notify_wifi_status 0x03 0x33, success\n");
			notify_wifi_status(0x03, 0x33);

			int ret = is_paring_success_flag();
			printf("is_paring_success_flag, ret = %d\n", ret);
			if (ret != 0) {
				set_save_ssid_pwd(ssid_array_info, passwd_array_info);
			}
		} else if (ret == -2) {
			printf("notify_wifi_status 0x03 0x31, wrong key\n");
			notify_wifi_status(0x03, 0x31);
		} else {
			printf("notify_wifi_status 0x03 0x34, connect failed\n");
			notify_wifi_status(0x03, 0x34);
		}

		notify_wifi_status(0x05, 0x37);
		ret = http_user_bind();
		if (ret == 0) {
			printf("http_user_bind success\n");
			notify_wifi_status(0x05, 0x35);
		} else {
			printf("http_user_bind failed\n");
			notify_wifi_status(0x05, 0x36);
		}
	}

done:
	gatt_db_attribute_write_result(attrib, id, ecode);
}

//response_type:0x03 status:
//0x30 complete receive wifi info
//0x31 wifi connect error, wrong pwd, wifi info error
//0x32 wifi connecting
//0x33 wifi connect success
//0x34 wifi connect failed

//response_type:0x05, status:
//0x37 user binding
//0x35 user bind success
//0x36 user bind error, 60s goto timeout
void notify_wifi_status(int response_type, int status) {
	char wifi_status_frame[5] = {0};
	wifi_status_frame[0] = 0x56;
	wifi_status_frame[1] = response_type;
	wifi_status_frame[2] = status;
	wifi_status_frame[3] = 0x00;
	wifi_status_frame[4] = ~(wifi_status_frame[1] + wifi_status_frame[2] + wifi_status_frame[3]) & 0xFF;
	
	bt_gatt_server_send_notification(server->gatt,
						server->hr_msrmt_handle,
						wifi_status_frame, 5, false);

}

static void confirm_write(struct gatt_db_attribute *attr, int err,
							void *user_data)
{
	if (!err)
		return;

	printf( "Error caching attribute %p - err: %d\n", attr, err);
	exit(1);
}

static void populate_hr_service(struct server *server)
{
	bt_uuid_t uuid;
	struct gatt_db_attribute *service, *hr_msrmt, *body;
	uint8_t body_loc = 1;  /* "Chest" */

	/* Add Heart Rate Service */
	bt_uuid16_create(&uuid, UUID_XIAOFEIXIA);
	service = gatt_db_add_service(server->db, &uuid, true, 8);
	server->hr_handle = gatt_db_attribute_get_handle(service);

	/* HR Measurement Characteristic */
	bt_uuid16_create(&uuid, UUID_XIAOFEIXIA_NOTIFY_STATUS);
	hr_msrmt = gatt_db_service_add_characteristic(service, &uuid,
						BT_ATT_PERM_NONE,
						BT_GATT_CHRC_PROP_NOTIFY,
						NULL, NULL, NULL);
	server->hr_msrmt_handle = gatt_db_attribute_get_handle(hr_msrmt);

	/* HR Control Point Characteristic */
	bt_uuid16_create(&uuid, UUID_XIAOFEIXIA_WRITE_WIFIINFO);
	gatt_db_service_add_characteristic(service, &uuid,
						BT_ATT_PERM_WRITE,
						BT_GATT_CHRC_PROP_WRITE,
						NULL, xf_write_wifiinfo_cb,
						server);

	if (server->hr_visible)
		gatt_db_service_set_active(service, true);
}

static struct server *server_create(int fd, uint16_t mtu, bool hr_visible)
{
	//struct server *server;
	size_t name_len = strlen(test_device_name);

	server = new0(struct server, 1);
	if (!server) {
		printf( "Failed to allocate memory for server\n");
		return NULL;
	}

	server->att = bt_att_new(fd, false);
	if (!server->att) {
		printf( "Failed to initialze ATT transport layer\n");
		goto fail;
	}

	if (!bt_att_set_close_on_unref(server->att, true)) {
		printf( "Failed to set up ATT transport layer\n");
		goto fail;
	}

	if (!bt_att_register_disconnect(server->att, att_disconnect_cb, NULL,
									NULL)) {
		printf( "Failed to set ATT disconnect handler\n");
		goto fail;
	}

	server->name_len = name_len + 1;
	server->device_name = malloc(name_len + 1);
	if (!server->device_name) {
		printf( "Failed to allocate memory for device name\n");
		goto fail;
	}

	memcpy(server->device_name, test_device_name, name_len);
	server->device_name[name_len] = '\0';

	server->fd = fd;
	server->db = gatt_db_new();
	if (!server->db) {
		printf( "Failed to create GATT database\n");
		goto fail;
	}

	server->gatt = bt_gatt_server_new(server->db, server->att, mtu, 0);
	if (!server->gatt) {
		printf( "Failed to create GATT server\n");
		goto fail;
	}

	server->hr_visible = hr_visible;

	if (verbose) {
		bt_att_set_debug(server->att, BT_ATT_DEBUG_VERBOSE,
						att_debug_cb, "att: ", NULL);
		bt_gatt_server_set_debug(server->gatt, gatt_debug_cb,
							"server: ", NULL);
	}

	/* Random seed for generating fake Heart Rate measurements */
	srand(time(NULL));

	/* bt_gatt_server already holds a reference */
	populate_hr_service(server);

	return server;

fail:
	gatt_db_unref(server->db);
	free(server->device_name);
	bt_att_unref(server->att);
	free(server);

	return NULL;
}

static void server_destroy(struct server *server)
{
	bt_gatt_server_unref(server->gatt);
	gatt_db_unref(server->db);
}

static int l2cap_le_att_listen_and_accept(bdaddr_t *src, int sec,
							uint8_t src_type)
{
	int sk, nsk;
	struct sockaddr_l2 srcaddr, addr;
	socklen_t optlen;
	struct bt_security btsec;
	char ba[18];

	sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
	if (sk < 0) {
		perror("Failed to create L2CAP socket");
		return -1;
	}

	/* Set up source address */
	memset(&srcaddr, 0, sizeof(srcaddr));
	srcaddr.l2_family = AF_BLUETOOTH;
	srcaddr.l2_cid = htobs(ATT_CID);
	srcaddr.l2_bdaddr_type = src_type;
	bacpy(&srcaddr.l2_bdaddr, src);

	if (bind(sk, (struct sockaddr *) &srcaddr, sizeof(srcaddr)) < 0) {
		perror("Failed to bind L2CAP socket");
		goto fail;
	}

	/* Set the security level */
	memset(&btsec, 0, sizeof(btsec));
	btsec.level = sec;
	if (setsockopt(sk, SOL_BLUETOOTH, BT_SECURITY, &btsec,
							sizeof(btsec)) != 0) {
		printf( "Failed to set L2CAP security level\n");
		goto fail;
	}

	if (listen(sk, 10) < 0) {
		perror("Listening on socket failed");
		goto fail;
	}

	printf("Started listening on ATT channel. Waiting for connections\n");

	memset(&addr, 0, sizeof(addr));
	optlen = sizeof(addr);
	nsk = accept(sk, (struct sockaddr *) &addr, &optlen);
	if (nsk < 0) {
		perror("Accept failed");
		goto fail;
	}

	ba2str(&addr.l2_bdaddr, ba);
	printf("Connect from %s\n", ba);
	close(sk);

	return nsk;

fail:
	close(sk);
	return -1;
}

static void print_uuid(const bt_uuid_t *uuid)
{
	char uuid_str[MAX_LEN_UUID_STR];
	bt_uuid_t uuid128;

	bt_uuid_to_uuid128(uuid, &uuid128);
	bt_uuid_to_string(&uuid128, uuid_str, sizeof(uuid_str));

	printf("%s\n", uuid_str);
}

static void signal_cb(int signum, void *user_data)
{
	switch (signum) {
	case SIGINT:
	case SIGTERM:
		printf("signal_cb, signum = %d\n", signum);
		mainloop_quit();
		break;
	default:
		break;
	}
}

#define POPEN_SIZE 1024
int system_cmd(char *cmd) {
    FILE *fp;
    char output[POPEN_SIZE];
    printf("system_cmd begin..., cmd = %s\n", cmd);

    fp = popen(cmd, "r");
    if (fp == NULL) {
        fprintf(stderr, "popen failed.\n");
        return -1;
    }

    while (fgets(output, POPEN_SIZE, fp) != NULL) {
        printf("%s", output);
    }

    printf("system_cmd end...\n");
    pclose(fp);
    return 0;
}

void ble_sn_add_space(char *dst, char *src, int src_size) {
    // int src_len = strlen(src);
	printf("src_size = %d\n", src_size);
	int pos = 0;
	for (int i = 0; i < src_size; i++) {
		pos += sprintf(dst + pos, " %0x", src[i]);
	}
	//printf("dst = %s\n", dst);
}

void ble_adv_set_and_on() {
	printf("ble_adv_set_and_on");
	char ble_reset_cmd[128] = "hcitool cmd 0x03 0x0003";
	char dev_sn[16] = "AF1234766BQ103D"; //dev_sn num is 15
	//char ble_adv_cmd[128] = "hcitool -i hci0 cmd 0x08 0x0008 1e 02 01 06 1a 09 59 5A 4C 44 2D 52 56 2D 53 2D 41 46 31 32 33 34 37 36 36 42 51 31 30 33 44 00";
	char ble_adv_cmd[128] = "hcitool -i hci0 cmd 0x08 0x0008 1e 02 01 06 1a 09 59 5A 4C 44 2D 52 56 2D 53 2D";

	char dev_sn_ex[48] = {0};
	ble_sn_add_space(dev_sn_ex, dev_sn, sizeof(dev_sn));
	printf("dev_sn_ex = %s\n", dev_sn_ex);
	strcat(ble_adv_cmd, dev_sn_ex);

	char ble_enable_cmd[128] = "hcitool -i hci0 cmd 0x08 0x000a 01";

	system_cmd(ble_reset_cmd);
	system_cmd(ble_adv_cmd);
	system_cmd(ble_enable_cmd);
}

int bluez5_main()
{
	bdaddr_t src_addr;
	int dev_id = 0;
	int fd;
	int sec = BT_SECURITY_LOW;
	uint8_t src_type = BDADDR_LE_PUBLIC;
	uint16_t mtu = 0;
	bool hr_visible = true;
	struct server *server;

	// start_wifi();
	ble_adv_set_and_on();

	if (hci_devba(dev_id, &src_addr) < 0) {
		perror("Adapter not available");
		return EXIT_FAILURE;
    }

	printf("dev_id = %d\n", dev_id);
	printf("src_addr = %x:%x:%x:%x:%x:%x\n", src_addr.b[0], src_addr.b[1], src_addr.b[2], src_addr.b[3], src_addr.b[4], src_addr.b[5]);

	fd = l2cap_le_att_listen_and_accept(&src_addr, sec, src_type);
	if (fd < 0) {
		printf( "Failed to accept L2CAP ATT connection\n");
		return EXIT_FAILURE;
	}

	mainloop_init();

	server = server_create(fd, mtu, hr_visible);
	if (!server) {
		close(fd);
		return EXIT_FAILURE;
	}

	printf("Running GATT server\n");

	mainloop_run_with_signal(signal_cb, NULL);

	printf("\n\nShutting down...\n");

	server_destroy(server);

	return EXIT_SUCCESS;
}
